package com.acquisition.task.service.impl;

import android.serialport.SerialPort;

import com.alibaba.fastjson.JSON;
import com.dcs.cn.common.constants.Constants;
import com.dcs.cn.common.constants.Hj212Constants;
import com.dcs.cn.common.service.Hj212Service;
import com.dcs.cn.common.service.SerialPortService;
import com.dcs.cn.common.util.ByteUtils;
import com.dcs.cn.common.util.ModbusUtils;
import com.dcs.cn.common.util.hj212util.hbt212.model.CpData;
import com.dcs.cn.common.util.hj212util.hbt212.model.Data;
import com.dcs.cn.common.util.hj212util.hbt212.model.Pollution;
import com.dcs.cn.data.domain.RealTimeMonitorFactor;
import com.dcs.cn.data.domain.SerialData;
import com.dcs.cn.data.service.SerialDataService;
import com.dcs.cn.data.service.impl.SerialDataServiceImpl;
import com.dcs.cn.param.domain.SerialPortParam;
import com.dcs.cn.param.domain.SingleMonitorFactorParam;
import com.dcs.cn.param.domain.SingleSerialPortParam;
import com.licheedev.modbus4android.ModbusParam;
import com.licheedev.modbus4android.param.SerialParam;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 串口
 * 业务逻辑层实现
 *
 * @author wuqiwei
 * @date 2021/02/22
 */
public class SerialPortServiceImpl implements SerialPortService {

    private static final Logger LOG = LoggerFactory.getLogger(SerialPortServiceImpl.class);

    @Override
    public RealTimeMonitorFactor readSerialPortForHj2017(SingleMonitorFactorParam monitorFactorParam, SerialPortParam serialPortParam, Long dataTime) {
        if (Constants.DEV_TTYS2.equals(monitorFactorParam.getPortCode())) {
            return readSpecialSerialPort(monitorFactorParam, serialPortParam, dataTime);
        } else {
            return readSerialPort(monitorFactorParam, serialPortParam, dataTime);
        }
    }

    @Override
    public RealTimeMonitorFactor readSerialPortForHj2005(SingleMonitorFactorParam monitorFactorParam, SerialPortParam serialPortParam, Long dataTime) {
        if (Constants.DEV_TTYS2.equals(monitorFactorParam.getPortCode())) {
            return readSpecialSerialPort(monitorFactorParam, serialPortParam, dataTime);
        } else {
            return readSerialPort(monitorFactorParam, serialPortParam, dataTime);
        }
    }

    @SuppressWarnings("BusyWait")
    @Override
    public RealTimeMonitorFactor readSerialPortForHebei(SingleMonitorFactorParam monitorFactorParam, SerialPortParam serialPortParam) {
        RealTimeMonitorFactor realTimeMonitorFactor = new RealTimeMonitorFactor();
        SingleSerialPortParam singleSerialPortParam = serialPortParam.getSerialPortMap().get(monitorFactorParam.getPortCode());
        if (singleSerialPortParam == null) {
            LOG.error("未查询到该串口参数：{}，的配置", monitorFactorParam.getPortCode());
            return realTimeMonitorFactor;
        }
        SerialDataService serialDataService = new SerialDataServiceImpl();
        Hj212Service hj212Service = new Hj212ServiceImpl();
        try {
            SerialPort serialPort = SerialPort.newBuilder(singleSerialPortParam.getPortCode(), singleSerialPortParam.getBaudRate())
                    .dataBits(singleSerialPortParam.getDataBits())
                    .parity(singleSerialPortParam.getParity())
                    .stopBits(singleSerialPortParam.getStopBits())
                    .build();
            long start = System.currentTimeMillis();
            for (int i = 0; i < 3; i++) {
                // 发送数据
                serialPort.getOutputStream().write(ByteUtils.hexStrToByteArr("0x23,0x23,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x11,0x22,0x26,0x26".replaceAll(",", "").replaceAll("0x", "")));
                Thread.sleep(500);
                // 读取数据
                InputStream inputStream = new BufferedInputStream(serialPort.getInputStream());
                while (true) {
                    if (inputStream.available() > 0) {
                        Thread.sleep(500);
                        byte[] buffer = new byte[inputStream.available()];
                        int size = inputStream.read(buffer);
                        String msg = ByteUtils.byteArrToHexStr(buffer);
                        LOG.info("读取监测因子{}河北省协议，串口号：{}，接收到河北省协议报文：{}", monitorFactorParam.getMonitorFactorKey(), singleSerialPortParam.getPortCode(), msg);
                        /*
                         * 初步校验：
                         * 1.报文以2323为包头，2626为包尾
                         * 2.校验数据包长度：数据包长度 = (报文总长度 / 2) - 包头位(2) - 包尾位(2) - 数据包长度位(2)
                         */
                        if (!msg.startsWith("2323") || !msg.endsWith("2626")) {
                            LOG.error("读取监测因子{}河北省协议，串口号：{}，包头包尾校验失败，再次重新读取，第{}次，接收的报文：{}", monitorFactorParam.getMonitorFactorKey(), singleSerialPortParam.getPortCode(), i, msg);
                            // 校验失败，再重新读取，开始时间重置
                            start = System.currentTimeMillis();
                            break;
                        }
                        if (Integer.parseInt(msg.substring(4, 8), 16) != msg.length() / 2 - 6) {
                            LOG.error("读取监测因子{}河北省协议，串口号：{}，数据包长度校验失败，再次重新读取，第{}次，报文中数据包长度：{}，计算出的数据包长度（报文总长度除2再减6）：{}，接收的报文：{}", monitorFactorParam.getMonitorFactorKey(), singleSerialPortParam.getPortCode(), i, Integer.parseInt(msg.substring(4, 8), 16), msg.length() / 2 - 6, msg);
                            // 校验失败，再重新读取，开始时间重置
                            start = System.currentTimeMillis();
                            break;
                        }
                        // 解析河北省协议
                        Map<String, String> map = hj212Service.hebeiTo2017Map(msg);
                        LOG.info("读取监测因子{}河北省协议，串口号：{}，解析完的串口数据：{}", monitorFactorParam.getMonitorFactorKey(), singleSerialPortParam.getPortCode(), JSON.toJSONString(map));
                        CpData cpData = JSON.parseObject(map.get("data"), CpData.class);
                        Pollution pollution = cpData != null ? cpData.getPollution().get(monitorFactorParam.getMonitorFactorKey()) : null;
                        if (pollution == null) {
                            LOG.error("读取监测因子{}河北省协议，未查询到该监测因子：{}，对应的串口数据，串口数据：cp：{}，再次重新读取，第{}次", monitorFactorParam.getMonitorFactorKey(), monitorFactorParam.getMonitorFactorKey(), JSON.toJSONString(cpData), i);
                            start = System.currentTimeMillis();
                            break;
                        }
                        if (pollution.getRtd() == null) {
                            LOG.error("读取监测因子{}河北省协议，未查询到该监测因子：{}，对应的实时数据，串口数据：cp：{}，pollution：{}，再次重新读取，第{}次", monitorFactorParam.getMonitorFactorKey(), monitorFactorParam.getMonitorFactorKey(), JSON.toJSONString(cpData), JSON.toJSONString(pollution), i);
                            start = System.currentTimeMillis();
                            break;
                        }
                        if (pollution.getSampleTime() != null && !"".equals(pollution.getSampleTime())) {
                            realTimeMonitorFactor.setSampleTime(Long.valueOf(pollution.getSampleTime()));
                        } else {
                            realTimeMonitorFactor.setSampleTime(Long.valueOf(cpData.getDataTime()));
                        }
                        if (pollution.getZsRtd() != null && !"".equals(pollution.getZsRtd().toString())) {
                            realTimeMonitorFactor.setZsRtd(pollution.getZsRtd().toString());
                        }
                        realTimeMonitorFactor.setMonitorFactor(monitorFactorParam.getMonitorFactorKey());
                        realTimeMonitorFactor.setRtd(pollution.getRtd().toString());
                        realTimeMonitorFactor.setFlag(pollution.getFlag());
                        serialPort.close();
                        return realTimeMonitorFactor;
                    }
                    if (System.currentTimeMillis() - start > 5000) {
                        LOG.error("读取监测因子{}河北省协议，串口号：{}，读取数据超时，再次重新读取，第{}次", monitorFactorParam.getMonitorFactorKey(), singleSerialPortParam.getPortCode(), i);
                        // 读取超时，再重新读取，开始时间重置
                        start = System.currentTimeMillis();
                        break;
                    }
                }
            }
            serialPort.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return realTimeMonitorFactor;
    }

    @Override
    public RealTimeMonitorFactor readSerialPortForModbusRtu(SingleMonitorFactorParam monitorFactorParam, SerialPortParam serialPortParam) {
        RealTimeMonitorFactor realTimeMonitorFactor = new RealTimeMonitorFactor();
        SingleSerialPortParam singleSerialPortParam = serialPortParam.getSerialPortMap().get(monitorFactorParam.getPortCode());
        if (singleSerialPortParam == null) {
            LOG.error("未查询到该串口参数：{}，的配置", monitorFactorParam.getPortCode());
            return realTimeMonitorFactor;
        }
        ModbusParam<SerialParam> modbusParam = SerialParam.create(singleSerialPortParam.getPortCode(), singleSerialPortParam.getBaudRate())
                .setDataBits(singleSerialPortParam.getDataBits())
                .setParity(singleSerialPortParam.getParity())
                .setStopBits(singleSerialPortParam.getStopBits());
        // 先关闭一下
        ModbusUtils.get().closeModbusMaster();
        try {
            // 初始化
            ModbusUtils.get().syncInit(modbusParam);
            int[] values = bytesToIntArray(ModbusUtils.get().syncReadHoldingRegisters(monitorFactorParam.getSlaveId(), monitorFactorParam.getRegisterAddress(), 1).getData());
            if (values.length <= 0) {
                LOG.error("读取modbus协议，读出来的数据为空");
                return realTimeMonitorFactor;
            }
            realTimeMonitorFactor.setMonitorFactor(monitorFactorParam.getMonitorFactorKey());
            realTimeMonitorFactor.setRtd(getMonitorFactorValue(values[0], monitorFactorParam.getDataType(), monitorFactorParam.getDecimalNum()));
            realTimeMonitorFactor.setFlag(Hj212Constants.HJ212_FLAG_N);
            return realTimeMonitorFactor;
        } catch (Exception e) {
            e.printStackTrace();
            return realTimeMonitorFactor;
        }
    }

    @Override
    public RealTimeMonitorFactor readSerialPortForW00000(SingleMonitorFactorParam monitorFactorParam, SerialPortParam serialPortParam) {
        RealTimeMonitorFactor realTimeMonitorFactor = new RealTimeMonitorFactor();
        try {
            SingleSerialPortParam singleSerialPortParam = serialPortParam.getSerialPortMap().get(monitorFactorParam.getPortCode());
            if (singleSerialPortParam == null) {
                LOG.error("未查询到该串口参数：{}，的配置", monitorFactorParam.getPortCode());
                return realTimeMonitorFactor;
            }
            Hj212Service hj212Service = new Hj212ServiceImpl();
            SerialPort serialPort = SerialPort.newBuilder(singleSerialPortParam.getPortCode(), singleSerialPortParam.getBaudRate())
                    .dataBits(singleSerialPortParam.getDataBits())
                    .parity(singleSerialPortParam.getParity())
                    .stopBits(singleSerialPortParam.getStopBits())
                    .build();
            // 发送数据
            serialPort.getOutputStream().write(0x11);
            Thread.sleep(500);
            // 读取数据
            InputStream inputStream = new BufferedInputStream(serialPort.getInputStream());
            long start = System.currentTimeMillis();
            while (true) {
                if (inputStream.available() > 0) {
                    byte[] buffer = new byte[inputStream.available()];
                    int size = inputStream.read(buffer);
                    String msg = new String(buffer, StandardCharsets.UTF_8);
                    if ("".equals(msg)) {
                        LOG.error("读取w00000，读出来的数据为空字符串");
                        return realTimeMonitorFactor;
                    }
                    String rtd;
                    msg = msg.split("\r\n")[0];
                    if (msg.length() > 1 && msg.length() < 10) {
                        // 金源协议
                        int value = Integer.parseInt(msg.replaceAll("\\r","").replaceAll("\\n","").replaceAll(" ",""));
                        rtd = getMonitorFactorValue(value, 1, 2);
                    } else if (msg.contains("OK")) {
                        // 九波协议
                        msg = msg.substring(msg.indexOf("Q"), msg.indexOf("\\("));
                        rtd = msg.replaceAll("Q","").replaceAll("\\(", "").replaceAll("=", "").replaceAll(" ","");
                    } else {
                        // 怡文协议
                        msg = msg.substring(msg.indexOf("Q"), msg.indexOf("L"));
                        rtd = msg.replaceAll("Q","").replaceAll("=","").replaceAll(" ","");
                    }
                    realTimeMonitorFactor.setMonitorFactor(monitorFactorParam.getMonitorFactorKey());
                    realTimeMonitorFactor.setRtd(rtd);
                    realTimeMonitorFactor.setFlag(Hj212Constants.HJ212_FLAG_N);
                    serialPort.close();
                    return realTimeMonitorFactor;
                }
                if (System.currentTimeMillis() - start > 5000) {
                    LOG.error("读取超时，读取w00000监测因子数据超时，断开连接");
                    serialPort.close();
                    return realTimeMonitorFactor;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return realTimeMonitorFactor;
        }
    }

    /**
     * 读取串口数据
     *
     * @param monitorFactorParam 监测因子参数
     * @param serialPortParam 串口参数
     * @param dataTime 数据时间
     * @return RealTimeMonitorFactor
     */
    private RealTimeMonitorFactor readSerialPort(SingleMonitorFactorParam monitorFactorParam, SerialPortParam serialPortParam, Long dataTime) {
        RealTimeMonitorFactor realTimeMonitorFactor = new RealTimeMonitorFactor();
        SingleSerialPortParam singleSerialPortParam = serialPortParam.getSerialPortMap().get(monitorFactorParam.getPortCode());
        if (singleSerialPortParam == null) {
            LOG.error("未查询到该串口参数：{}，的配置", monitorFactorParam.getPortCode());
            return realTimeMonitorFactor;
        }
        SerialDataService serialDataService = new SerialDataServiceImpl();
        Map<String, Object> param = new HashMap<>(16);
        param.put("AND_EQ_type", monitorFactorParam.getProtocolType());
        param.put("AND_EQ_portCode", monitorFactorParam.getPortCode());
        List<SerialData> list = serialDataService.queryByExample(param);
        if (list == null || list.size() <= 0) {
            LOG.error("未查询到该串口:{}，协议类型:{} 的串口数据", monitorFactorParam.getPortCode(), monitorFactorParam.getProtocolType());
            return realTimeMonitorFactor;
        }
        Data data = JSON.parseObject(list.get(0).getData(), Data.class);
        Pollution pollution = data.getCp().getPollution().get(monitorFactorParam.getMonitorFactorKey());
        if (pollution == null) {
            LOG.error("未查询到该监测因子：{}，串口数据：cp：{}", monitorFactorParam.getMonitorFactorKey(), JSON.toJSONString(data.getCp().getPollution()));
            return realTimeMonitorFactor;
        }
        if (pollution.getRtd() == null) {
            LOG.error("未查询到该监测因子：{}，对应的实时数据，串口数据：cp：{}，pollution：{}", monitorFactorParam.getMonitorFactorKey(), JSON.toJSONString(data.getCp().getPollution()), JSON.toJSONString(pollution));
            return realTimeMonitorFactor;
        }
        if (pollution.getSampleTime() != null && !"".equals(pollution.getSampleTime())) {
            realTimeMonitorFactor.setSampleTime(Long.valueOf(pollution.getSampleTime()));
        } else {
            realTimeMonitorFactor.setSampleTime(Long.valueOf(data.getCp().getDataTime()));
        }
        if (pollution.getZsRtd() != null && !"".equals(pollution.getZsRtd().toString())) {
            realTimeMonitorFactor.setZsRtd(pollution.getZsRtd().toString());
        }
        realTimeMonitorFactor.setMonitorFactor(monitorFactorParam.getMonitorFactorKey());
        realTimeMonitorFactor.setRtd(pollution.getRtd().toString());
        realTimeMonitorFactor.setFlag(pollution.getFlag());
        return realTimeMonitorFactor;
    }

    /**
     * 读取特殊串口数据，该串口含有多条监测因子的数据
     *
     * @param monitorFactorParam 监测因子参数
     * @param serialPortParam 串口参数
     * @param dataTime 数据时间
     * @return RealTimeMonitorFactor
     */
    private RealTimeMonitorFactor readSpecialSerialPort(SingleMonitorFactorParam monitorFactorParam, SerialPortParam serialPortParam, Long dataTime) {
        RealTimeMonitorFactor realTimeMonitorFactor = new RealTimeMonitorFactor();
        SingleSerialPortParam singleSerialPortParam = serialPortParam.getSerialPortMap().get(monitorFactorParam.getPortCode());
        if (singleSerialPortParam == null) {
            LOG.error("未查询到该串口参数：{}，的配置", monitorFactorParam.getPortCode());
            return realTimeMonitorFactor;
        }
        SerialDataService serialDataService = new SerialDataServiceImpl();
        Map<String, Object> param = new HashMap<>(16);
        param.put("AND_EQ_portCode", monitorFactorParam.getPortCode());
        param.put("AND_LIKE_monitorFactorKey", monitorFactorParam.getMonitorFactorKey());
        List<SerialData> list = serialDataService.queryByExample(param);
        if (list == null || list.size() <= 0) {
            LOG.error("未查询到该串口:{}，监测因子：{} 的串口数据", monitorFactorParam.getPortCode(), monitorFactorParam.getMonitorFactorKey());
            return realTimeMonitorFactor;
        }
        Data data = JSON.parseObject(list.get(0).getData(), Data.class);
        Pollution pollution = data.getCp().getPollution().get(monitorFactorParam.getMonitorFactorKey());
        if (pollution == null) {
            LOG.error("未查询到该监测因子：{}，串口数据：cp：{}", monitorFactorParam.getMonitorFactorKey(), JSON.toJSONString(data.getCp().getPollution()));
            return realTimeMonitorFactor;
        }
        if (pollution.getRtd() == null) {
            LOG.error("未查询到该监测因子：{}，对应的实时数据，串口数据：cp：{}，pollution：{}", monitorFactorParam.getMonitorFactorKey(), JSON.toJSONString(data.getCp().getPollution()), JSON.toJSONString(pollution));
            return realTimeMonitorFactor;
        }
        if (pollution.getSampleTime() != null && !"".equals(pollution.getSampleTime())) {
            realTimeMonitorFactor.setSampleTime(Long.valueOf(pollution.getSampleTime()));
        } else {
            realTimeMonitorFactor.setSampleTime(Long.valueOf(data.getCp().getDataTime()));
        }
        if (pollution.getZsRtd() != null && !"".equals(pollution.getZsRtd().toString())) {
            realTimeMonitorFactor.setZsRtd(pollution.getZsRtd().toString());
        }
        realTimeMonitorFactor.setMonitorFactor(monitorFactorParam.getMonitorFactorKey());
        realTimeMonitorFactor.setRtd(pollution.getRtd().toString());
        realTimeMonitorFactor.setFlag(pollution.getFlag());
        return realTimeMonitorFactor;
    }

    /**
     * 字节数组转int数组
     *
     * @param bytes 字节数组
     * @return int数组
     */
    private int[] bytesToIntArray(byte[] bytes) {
        int[] arr = new int[bytes.length / 2];
        for (int i = 0, j = 0; i < arr.length; i++, j += 2) {
            arr[i] = ((bytes[j] & 0xff) << 8) | (bytes[j + 1] & 0xff);
        }
        return arr;
    }

    /**
     * 监测因子数值处理
     *
     * @param value 监测因子数值
     * @param dataType 是否为小数
     * @param decimalNum 小数点个数
     * @return 小数处理完的数值
     */
    private String getMonitorFactorValue(int value, int dataType, int decimalNum) {
        StringBuilder sb = new StringBuilder(Integer.toString(value));
        if (dataType != 1) {
            return sb.toString();
        }
        // 对value值添加小数点
        if (sb.length() > decimalNum) {
            // 该情况例：'12' 1位小数时  值应为'1.2'
            sb.insert(sb.length() - decimalNum, '.');
            return sb.toString();
        } else if (sb.length() == decimalNum) {
            // 该情况例：'12' 2位小数时  值应为'0.12'
            sb.insert(0, "0");
            sb.insert(sb.length() - decimalNum, '.');
            return sb.toString();
        } else {
            // 该情况例：'12' 3位小数时  值应为'0.012'
            int count = decimalNum - sb.length() + 1;
            for (int i = 0; i < count; i++) {
                sb.insert(0, "0");
            }
            sb.insert(1, '.');
            return sb.toString();
        }
    }

}
